import 'package:flutter/material.dart';
import 'package:my_flutter_core/my_flutter_core.dart';

class SystemSetting {
  SystemSetting({this.generalSetting, this.themeSetting}) {
    generalSetting ??= GeneralSetting();
    themeSetting ??= ThemeSetting();
  }

  /// Works with [LocalStorage] and the [json] Map format defined by us.
  factory SystemSetting.fromJson(Map<String, dynamic> json) {
    return SystemSetting(
      generalSetting: tryToConvert<GeneralSetting>(
        json['generalSetting'],
        (p) => GeneralSetting.fromJson(p),
      ),
      themeSetting: tryToConvert<ThemeSetting>(
        json['themeSetting'],
        (p) => ThemeSetting.fromJson(p),
      ),
    );
  }

  /// Works with [LocalStorage] and the [json] Map format defined by us.
  factory SystemSetting.fromFlatJson(Map<String, dynamic> json) {
    final result = SystemSetting.fromJson(json);

    result.generalSetting = tryToConvert<GeneralSetting>(
      json,
      (p) => GeneralSetting.fromJson(p),
    );

    result.themeSetting = tryToConvert<ThemeSetting>(
      json,
      (p) => ThemeSetting.fromJson(p),
    );

    return result;
  }

  GeneralSetting? generalSetting;
  ThemeSetting? themeSetting;

  /// Converts to [Map<String, dynamic>] and works with [LocalStorage].
  Map<String, dynamic> toJson() {
    return {
      'generalSetting': generalSetting?.toJson(),
      'themeSetting': themeSetting?.toJson(),
    };
  }

  /// Converts to [Map<String, dynamic>] and works with [LocalStorage].
  /// FlatJson format contains NO hierarchy. It means NO nested object.
  ///
  /// This is for a reason:
  /// We might change the setting items to different properties. The data will be missing if we do so whild loading from app.
  /// Example:
  /// Json format before change:
  /// {
  ///   'generalSetting' : { },
  ///   'primaryColorIndex': 0,
  ///   'favoriteColorIndexes': [],
  /// }
  ///
  /// Json format after change:
  /// {
  ///   'generalSetting' : { },
  ///   'themeSetting': {
  ///     'enableMaterialDesign3': false,
  ///     'primaryColorIndex': 0,
  ///     'favoriteColorIndexes': [],
  ///   }
  /// }
  ///
  /// Flat json format will avoid such issue.
  Map<String, dynamic> toFlatJson() {
    Map<String, dynamic> json = {};

    json.addAll(generalSetting?.toJson() ?? {});
    json.addAll(themeSetting?.toJson() ?? {});

    return json;
  }
}

class GeneralSetting {
  GeneralSetting({this.doublePressExit = doublePressExitDefaultValue});

  /// Works with [LocalStorage] and the [json] Map format defined by us.
  factory GeneralSetting.fromJson(Map<String, dynamic> json) {
    return GeneralSetting(
      doublePressExit: tryToConvert<bool>(
        json['doublePressExit'],
        (p) => p as bool,
        defaultValue: doublePressExitDefaultValue,
      )!,
    );
  }

  static const doublePressExitDefaultValue = true;

  bool doublePressExit;

  /// Converts to [Map<String, dynamic>] and works with [LocalStorage].
  Map<String, dynamic> toJson() {
    return {'doublePressExit': doublePressExit};
  }
}

class ThemeSetting {
  ThemeSetting({
    this.primaryColor = defalutPrimaryColor,
    List<Color>? favoriteColors,
  }) : favoriteColors = favoriteColors ?? [];

  /// Works with [LocalStorage] and the [json] Map format defined by us.
  factory ThemeSetting.fromJson(Map<String, dynamic> json) {
    return ThemeSetting(
      primaryColor: tryToConvert<Color>(
        json['primaryColorIndex'],
        (p) => Color(p),
        defaultValue: defalutPrimaryColor,
      )!,
      favoriteColors:
          tryToConvert<List<Color>>(
                json['favoriteColorIndexes'],
                (p) => List<Color>.from(
                  p.split(_stringListSeparator).map((e) => Color(int.parse(e))),
                ),
                defaultValue: <Color>[],
              )
              as List<Color>,
    );
  }

  static const bool defaultUseMaterial3 = false;
  static const Color defalutPrimaryColor = Colors.blue;
  static const String _stringListSeparator = ',';

  Color primaryColor;
  List<Color> favoriteColors;

  /// Converts to [Map<String, dynamic>] and works with [LocalStorage].
  Map<String, dynamic> toJson() {
    return {
      'primaryColorIndex': primaryColor.toARGB32(),
      'favoriteColorIndexes': favoriteColors
          .map((e) => e.toARGB32())
          .join(_stringListSeparator),
    };
  }
}
